#include <map>
#include <fstream>
#include <unistd.h>
#include <dirent.h>

using namespace std;

#include "coapservertask.h"
#include "coapconstants.h"
#include "options.h"

///////////////////////////////////////////////
// Private methods
//////////////////////////////////////////////

bool CoapServerTask::getFile(std::string uripath, Frame& data)
{
	std::string tmp;
	std::cout << " >>> URIPATH: " << uripath;
	if(uripath[uripath.length()-1] == '/')
	//list directory
	{
		std::cout << " >>> LIST DIRECTORY" << std::endl;
		DIR* dir;
		struct dirent* record;
		dir= opendir(uripath.c_str());
		if(dir)
		{
			//read everything in directory and put it to data
			while(record= readdir(dir))
			{
				data.putLast((const Byte*) record->d_name, strlen(record->d_name));
				data.putLast('\n');
			}
			closedir(dir);
		}
	}
	else
	//read file
	{
		std::cout << " >>> GET FILE" << std::endl;	
		ifstream ifile(uripath.c_str());
		if(ifile.is_open())
		{
			//read all file until EOF
			while(!ifile.eof())
			{
				data.putLast((Byte) ifile.get());
			}
		}
		ifile.close();
	}
	return 1;
}

bool CoapServerTask::putFile(std::string uripath, Frame& data)
{
   ofstream oFile;

   // TODO: check if file already exists - now: truncate file
   oFile.open(uripath.c_str(), ios_base::trunc);
   std::string tmp = "";
   for (int ii = 0; ii < data.length(); ++ii)
      tmp += data.read(ii);
   oFile << tmp << std::endl;
   oFile.close();

   return 1;
}

bool CoapServerTask::delFile(std::string uripath)
{
   std::string cmd = "rm " + uripath;
   return system(cmd.c_str());
}

void CoapServerTask::serveRequest(Uint8 requestCode, Frame& payload)
{
   // TODO: URI construction from options
   Frame data;
   std::string uripath = getOptions().getOption(OPT_URI_PATH);
   switch (requestCode) {
      case METHOD_GET:
	 if (getFile(uripath, data))
	    sendACK(RES_CODE_CONTENT, data);
	 else 
	    sendACK(RES_CODE_NOT_FOUND, data);	    
	 break;
      case METHOD_PUT:
	 if (putFile(uripath, payload))
	    sendACK(RES_CODE_CREATED, data);
	 else
	    sendACK(RES_CODE_UNAUTHORIZED, data);
	 break;
      case METHOD_DELETE:
	 if (delFile(uripath))
	    sendACK(RES_CODE_DELETED, data);
	 else
	    sendACK(RES_CODE_NOT_FOUND, data);	    
	 break;
      case METHOD_POST:
	 sendACK(RES_CODE_NOT_IMPLEMENTED, data);
	 break;
      default:
	 sendACK(RES_CODE_NOT_IMPLEMENTED, data);
	 break;
   };
}

Uint16 CoapServerTask::getMsgID(void) const
{
   return msgID_;
}

void CoapServerTask::setMsgID(Uint16 msgID)
{
   msgID_ = msgID;
}

void CoapServerTask::sendACK(Uint8 responseCode, Frame& payload)
{
  CoapPeer::ACK *pdu = 
    (CoapPeer::ACK *) peer.create(CoapPeer::ack);
  if (!pdu) {
    return;
  }
  //////////////////
  // Header 4 bytes
  //////////////////
  // First byte
  Uint8 temp = HEADER_COAP_VERSION 
     | HEADER_TYPE_ACK
     | HEADER_OC_ACK;
  pdu->header0 = temp;
  // Second byte
  pdu->code = responseCode;
  // Third & fourth byte
  pdu->msgID = getMsgID();
  
  // Options: token 
  Uint8 option= OPT_TOKEN // delta
     | (TOKEN_SIZE << 4);
  pdu->options.putLast(option);
  // Option value
  pdu->options.putLast((const Byte*) getOptions().getOption(OPT_TOKEN).data(), 
		       getOptions().getOption(OPT_TOKEN).length());

  //std::cout << "*************************************************" << std::endl; 
  //std::cout << "Option: " 
  //	    << std::hex << static_cast<int>(pdu->options.read(0)) 
  //	    << " " 
  //	    << std::hex << static_cast<int>(pdu->options.read(1)) << std::endl;
  //std::cout << "*************************************************" << std::endl; 

  // Payload
  // TODO: human readable error description
  

  pdu->payload.combine(payload);

  pdu->addr = clientaddr_;
  
  peer.putMessage(pdu);

  std::cout << "***************SENDING ACK***********************" << std::endl; 
}

///////////////////////////////////////////////
// Public methods
//////////////////////////////////////////////

CoapServerTask::CoapServerTask(std::string name, Scheduler* s, CoapServerTaskSM* sm,
		       InetAddr clientaddr)
  : PTask(name, s, sm), 
    up(this),           
    peer(this, &down),  
    down(this, &peer),
    options_(),
    msgID_(0),
    clientaddr_(clientaddr) 
{
  // These are usually SHOULD
  STATIC_SYMBOL(up, this);
  STATIC_SYMBOL(down, this);
  STATIC_SYMBOL(peer, this);
}

CoapServerTask::~CoapServerTask()
{
  // Delete symbols from the hierarchy
  DELETE_SYMBOL(&up);
  DELETE_SYMBOL(&down);
  DELETE_SYMBOL(&peer);
}

//////////////
// Idle state
//////////////
bool CoapServerTask::idle_CON(Message* msg)
{
   std::cout << "******************************* INCOMMING CON REQUEST *********************************" << std::endl; 
   
   // Get PDU
   CoapPeer::CON *pdu = (CoapPeer::CON *)msg;

   // Create primitive and  Post primitive to user
   CoapServerSAP::Conind* primitive = (CoapServerSAP::Conind *) up.create(CoapServerSAP::conind);
   primitive->requestCode = pdu->code;
   up.putMessage(primitive);

   // Process request here
   setMsgID(pdu->msgID);
   getOptions().parseOptions(pdu->options, (pdu->header0 & 0xf0) >> 4);

   //TODO isCriticalOK
   //if (!getOptions().isCriticalOK()) {
   //   sleep(1); // Sleep for 1 sec
   //   Frame data;
   //   sendACK(RES_CODE_BAD_OPTION, data);
   //   return false;
   //}
   
   serveRequest(pdu->code, pdu->payload);

   return false;
}

bool CoapServerTask::idle_NON(Message* msg)
{
   // Get PDU
   CoapPeer::NON *pdu = (CoapPeer::NON *)msg;
   
   // TODO: Process request here
   
   // Create primitive
   CoapServerSAP::Nonind* primitive = 
      (CoapServerSAP::Nonind *) up.create(CoapServerSAP::nonind);

   primitive->requestCode = pdu->code;
   
   // Post primitive to user
   up.putMessage(primitive);
   
   return false;
}

Options& CoapServerTask::getOptions(void)
{
   return options_;
} 

